import React, { useState, useRef, useCallback, useEffect } from 'react';
import ReactFlow, { addEdge, MiniMap, Controls, Background, useNodesState, useEdgesState, Position, Handle } from 'react-flow-renderer';
import Sidebar from './components/Sidebar';
import NodeModal from './components/NodeModal';
import './index.css';

// 动态计算后端 API 的基地址
const getApiBaseUrl = () => {
  const protocol = window.location.protocol; // 'http:' or 'https:'
  const hostname = window.location.hostname; // '127.0.0.1', 'localhost', or a server IP
  const backendPort = 5000; // 后端服务的端口
  return `${protocol}//${hostname}:${backendPort}`;
};
const API_BASE_URL = getApiBaseUrl();

const initialNodes = [];
const initialEdges = [];

let idCounter = 0;
const getId = (existingNodes) => {
  const maxId = existingNodes.reduce((max, node) => {
    const idNum = parseInt(node.id.split('_')[1] || '0', 10);
    return Math.max(max, idNum);
  }, idCounter);
  idCounter = maxId + 1;
  return `node_${idCounter}`;
};

const getNodeTypeClass = (type) => {
  switch (type) {
    case 'start': return 'start';
    case 'llm': return 'llm';
    case 'branch': return 'branch';
    case 'code': return 'code';
    case 'http': return 'http';
    case 'knowledge': return 'knowledge';
    case 'end': return 'end';
    default: return '';
  }
};

// 自定义节点组件
const StartNodeComponent = ({ data }) => {
  const variableNames = Object.keys(data.variables || {}).join(', ');
  return (
    <div className="start-node">
      <Handle type="source" position={Position.Right} />
      <div className="node-name">{data.label}</div>
      <div><span className="text-blue-500">{variableNames ? '{x}' : ''}</span> {variableNames || '无'}</div>
    </div>
  );
};

const LLMNodeComponent = ({ data }) => {
  return (
    <div className="llm-node">
      <Handle type="target" position={Position.Left} />
      <Handle type="source" position={Position.Right} />
      <div className="node-name">{data.label}</div>
      <div>{data.model_name || '未指定'}</div>
    </div>
  );
};

const BranchNodeComponent = ({ data }) => {
  return (
    <div className="branch-node">
      <Handle type="target" position={Position.Left} />
      <div className="node-name">{data.label}</div>
      <div>
        {data.variable ? (
          <>
            <span className="text-blue-500">{'{x}'}</span> {data.variable} {data.conditionType || ''} {data.conditionValue || ''}
          </>
        ) : (
          '未指定'
        )}
      </div>
      <Handle type="source" position={Position.Right} id="true_branch" style={{ top: '20%' }}>
        <span className="branch-label">if</span>
      </Handle>
      <Handle type="source" position={Position.Right} id="false_branch" style={{ top: '80%' }}>
        <span className="branch-label">else</span>
      </Handle>
    </div>
  );
};

const CodeNodeComponent = ({ data }) => {
  return (
    <div className="code-node">
      <Handle type="target" position={Position.Left} />
      <Handle type="source" position={Position.Right} />
      <div className="node-name">{data.label}</div>
      <div>{data.code ? '已配置代码' : '未配置代码'}</div>
    </div>
  );
};

const HttpNodeComponent = ({ data }) => {
  return (
    <div className="http-node">
      <Handle type="target" position={Position.Left} />
      <Handle type="source" position={Position.Right} />
      <div className="node-name">{data.label}</div>
      <div>{data.url ? data.method + ' ' + data.url : '未配置URL'}</div>
    </div>
  );
};

const KnowledgeNodeComponent = ({ data }) => {
  // 获取知识库名称，如果未选择则显示“未选择”
  const knowledgeBaseName = data.knowledge_base_id ? data.knowledge_base_name || '已选择' : '未选择';

  return (
    <div className="knowledge-node">
      <Handle type="target" position={Position.Left} />
      <Handle type="source" position={Position.Right} />
      <div className="node-name">{data.label}</div>
      <div>知识库: {knowledgeBaseName}</div>
    </div>
  );
};

const EndNodeComponent = ({ data }) => {
  const variableNames = Object.keys(data.variables || {}).join(', ');
  return (
    <div className="end-node">
      <Handle type="target" position={Position.Left} />
      <div className="node-name">{data.label}</div>
      <div><span className="text-blue-500">{variableNames ? '{x}' : ''}</span> {variableNames || '无'}</div>
    </div>
  );
};

const nodeTypes = {
  start: StartNodeComponent,
  llm: LLMNodeComponent,
  branch: BranchNodeComponent,
  code: CodeNodeComponent,
  http: HttpNodeComponent,
  knowledge: KnowledgeNodeComponent,
  end: EndNodeComponent,
};

// API Modal Component
const ApiModal = ({ isOpen, onClose, apiDetails }) => {
  if (!isOpen) return null;
  const [isCopied, setIsCopied] = useState(false);

  const curlCommand = `curl -X POST ${API_BASE_URL}${apiDetails.endpoint} \\
  -H "Content-Type: application/json" \\
  -H "X-API-Key: ${apiDetails.api_key}" \\
  -d '{"key": "value"}'`;

  const copyToClipboard = () => {
    navigator.clipboard.writeText(curlCommand);
    setIsCopied(true);
    setTimeout(() => setIsCopied(false), 2000);
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content api-modal" onClick={(e) => e.stopPropagation()}>
        <h3 className="text-lg font-semibold mb-4">API 详情</h3>
        <p className="text-sm text-gray-600 mb-2">工作流 ID: {apiDetails.workflow_id}</p>
        <p className="text-sm text-gray-600 mb-4">接口地址: {API_BASE_URL}{apiDetails.endpoint}</p>
        <label className="block mb-2 text-sm font-medium text-gray-700">cURL 命令:</label>
        <div className="relative">
          <pre className="p-3 bg-gray-800 text-gray-100 rounded-md font-mono text-sm overflow-x-auto">
            {curlCommand}
          </pre>
          <button
            onClick={copyToClipboard}
            disabled={isCopied}
            className={`absolute top-2 right-2 px-2 py-1 text-white text-xs rounded-md transition-colors ${
              isCopied
                ? 'bg-green-600 cursor-not-allowed'
                : 'bg-blue-500 hover:bg-blue-600'
            }`}
          >
            {isCopied ? '已复制' : '复制'}
          </button>
        </div>
        <p className="text-xs text-gray-500 mt-2">使用此 cURL 命令调用 API。请根据需要替换 JSON 请求体。</p>
        <div className="mt-4 flex justify-end">
          <button
            onClick={onClose}
            className="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600"
          >
            关闭
          </button>
        </div>
      </div>
    </div>
  );
};

const KnowledgeBaseModal = ({ isOpen, onClose, apiBaseUrl }) => {
  if (!isOpen) return null;

  const [knowledgeBases, setKnowledgeBases] = useState([]);
  const [newKbName, setNewKbName] = useState('');
  const [inputType, setInputType] = useState('text');
  const [newKbContent, setNewKbContent] = useState('');
  const [file, setFile] = useState(null);
  const [fileUrl, setFileUrl] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [uploadStatus, setUploadStatus] = useState('');

  // 新增状态，用于管理文档切片展示
  const [selectedKb, setSelectedKb] = useState(null);
  const [kbChunks, setKbChunks] = useState([]);
  const [isChunksLoading, setIsChunksLoading] = useState(false);
  const [chunksError, setChunksError] = useState('');

  const fetchKbs = useCallback(async () => {
    setIsLoading(true);
    setError('');
    try {
      const response = await fetch(`${apiBaseUrl}/knowledge-bases`);
      if (!response.ok) throw new Error('无法获取知识库列表');
      const data = await response.json();
      setKnowledgeBases(data);
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  }, [apiBaseUrl]);

  useEffect(() => {
    if (isOpen) {
      fetchKbs();
      // 关闭时重置选择
      return () => {
        setSelectedKb(null);
        setKbChunks([]);
      };
    }
  }, [isOpen, fetchKbs]);

  const handleSelectKb = useCallback(async (kb) => {
    if (selectedKb?.id === kb.id) {
        // 如果再次点击同一个，则取消选择
        setSelectedKb(null);
        setKbChunks([]);
        return;
    }
    setSelectedKb(kb);
    setIsChunksLoading(true);
    setChunksError('');
    setKbChunks([]);
    try {
        const response = await fetch(`${apiBaseUrl}/knowledge-bases/${kb.id}/documents`);
        if (!response.ok) {
            const errData = await response.json();
            throw new Error(errData.error || '无法获取文档切片');
        }
        const data = await response.json();
        setKbChunks(data.documents || []);
    } catch (err) {
        setChunksError(err.message);
    } finally {
        setIsChunksLoading(false);
    }
  }, [apiBaseUrl, selectedKb]);


  const handleCreate = async (e) => {
    e.preventDefault();
    setError('');
    setUploadStatus('');
    setIsLoading(true);

    try {
        const formData = new FormData();
        formData.append('name', newKbName);
        formData.append('sourceType', inputType);

        if (inputType === 'text') {
            if (!newKbContent.trim()) throw new Error('请输入文档内容');
            formData.append('content', newKbContent);
        } else if (inputType === 'file') {
            if (!file) throw new Error('请选择一个文件');
            formData.append('file', file);
        } else if (inputType === 'url') {
            if (!fileUrl.trim()) throw new Error('请输入文件URL');
            formData.append('url', fileUrl);
        }
        
        if (!newKbName.trim()) throw new Error('请输入知识库名称');

        const response = await fetch(`${apiBaseUrl}/knowledge-bases`, {
            method: 'POST',
            body: formData,
        });

        if (!response.ok) {
            const errData = await response.json();
            throw new Error(errData.error || '创建知识库失败');
        }

        setNewKbName('');
        setNewKbContent('');
        setFile(null);
        setFileUrl('');
        setInputType('text');
        await fetchKbs();
        setUploadStatus('创建成功!');
    } catch (err) {
        setError(err.message);
    } finally {
        setIsLoading(false);
    }
  };

  const handleDelete = async (kbId) => {
    if (!window.confirm('确定要删除这个知识库吗？此操作不可逆。')) return;
    setIsLoading(true);
    setError('');
    try {
      const response = await fetch(`${apiBaseUrl}/knowledge-bases/${kbId}`, {
        method: 'DELETE',
      });
      if (!response.ok) {
        const errData = await response.json();
        throw new Error(errData.error || '删除知识库失败');
      }
      // 如果删除的是当前选中的知识库，则清空右侧面板
      if(selectedKb?.id === kbId) {
        setSelectedKb(null);
        setKbChunks([]);
      }
      await fetchKbs();
    } catch (err) {
      setError(err.message);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="kb-modal-content wide" onClick={(e) => e.stopPropagation()}>
        <h3 className="text-xl font-semibold mb-4 text-gray-800 border-b pb-3">知识库管理</h3>
        <div className="flex-grow flex gap-6 overflow-hidden">
          {/* Left Pane */}
          <div className="w-1/2 flex flex-col overflow-y-auto pr-4 custom-scrollbar">
              {/* Existing KBs List */}
              <div>
                <h4 className="text-lg font-medium mb-2 text-gray-700">现有知识库</h4>
                {isLoading && <p>加载中...</p>}
                {error && !isLoading && <p className="text-red-500">{error}</p>}
                {knowledgeBases.length > 0 ? (
                  <ul className="space-y-2">
                    {knowledgeBases.map(kb => (
                      <li key={kb.id} 
                          className={`flex justify-between items-center p-3 rounded-md border cursor-pointer transition-all duration-200 ${selectedKb?.id === kb.id ? 'bg-blue-100 border-blue-400 shadow-sm' : 'bg-gray-50 hover:bg-gray-100'}`}
                          onClick={() => handleSelectKb(kb)}
                      >
                        <span className="font-medium text-gray-800">{kb.name}</span>
                        <button
                          onClick={(e) => { e.stopPropagation(); handleDelete(kb.id); }}
                          className="px-3 py-1 bg-red-500 text-white text-sm rounded-md hover:bg-red-600"
                          disabled={isLoading}
                        >
                          删除
                        </button>
                      </li>
                    ))}
                  </ul>
                ) : (
                  !isLoading && <p className="text-gray-500">还没有知识库。</p>
                )}
              </div>

              {/* Create New KB Form */}
              <div className="mt-6 border-t pt-6">
                <h4 className="text-lg font-medium mb-3 text-gray-700">创建新知识库</h4>
                <form onSubmit={handleCreate} className="space-y-4">
                  <input
                    type="text"
                    value={newKbName}
                    onChange={(e) => setNewKbName(e.target.value)}
                    placeholder="知识库名称"
                    className="w-full p-2 border border-gray-300 rounded-md"
                    disabled={isLoading}
                    required
                  />
                  <div className="flex gap-4 mb-4 border-b pb-4">
                    <label className="flex items-center gap-1 cursor-pointer">
                      <input type="radio" name="inputType" value="text" checked={inputType === 'text'} onChange={() => setInputType('text')} disabled={isLoading}/>
                      文本输入
                    </label>
                    <label className="flex items-center gap-1 cursor-pointer">
                      <input type="radio" name="inputType" value="file" checked={inputType === 'file'} onChange={() => setInputType('file')} disabled={isLoading}/>
                      上传文件
                    </label>
                    <label className="flex items-center gap-1 cursor-pointer">
                      <input type="radio" name="inputType" value="url" checked={inputType === 'url'} onChange={() => setInputType('url')} disabled={isLoading}/>
                      文件URL
                    </label>
                  </div>

                  {inputType === 'text' && ( <textarea value={newKbContent} onChange={(e) => setNewKbContent(e.target.value)} placeholder="在此处粘贴文档内容，每行一个文档。" rows="8" className="w-full p-2 border border-gray-300 rounded-md font-mono text-sm" disabled={isLoading}/> )}
                  {inputType === 'file' && ( <div> <input type="file" onChange={(e) => setFile(e.target.files[0])} className="w-full p-2 border border-gray-300 rounded-md" disabled={isLoading}/> </div> )}
                  {inputType === 'url' && ( <input type="url" value={fileUrl} onChange={(e) => setFileUrl(e.target.value)} placeholder="请输入文件URL" className="w-full p-2 border border-gray-300 rounded-md" disabled={isLoading}/> )}
                  {uploadStatus && <p className="text-sm text-green-600 mt-1">{uploadStatus}</p>}

                  <div className="flex justify-end gap-3 pt-4">
                    <button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:bg-blue-300" disabled={isLoading}>
                      {isLoading ? '创建中...' : '创建知识库'}
                    </button>
                  </div>
                </form>
              </div>
          </div>
          {/* Right Pane */}
          <div className="w-1/2 flex-1 border-l border-gray-200 pl-6 flex flex-col">
            {selectedKb ? (
              <>
                <h4 className="text-lg font-medium mb-3 text-gray-700 flex-shrink-0">文档切片: <span className='font-bold text-blue-600'>{selectedKb.name}</span></h4>
                <div className="flex-grow overflow-y-auto pr-2 custom-scrollbar">
                  {isChunksLoading && <p className="text-gray-500">正在加载文档内容...</p>}
                  {chunksError && <p className="text-red-500">错误: {chunksError}</p>}
                  {!isChunksLoading && !chunksError && (
                    kbChunks.length > 0 ? (
                      <ol className="list-decimal list-inside space-y-3 text-sm text-gray-800">
                        {kbChunks.map((chunk, index) => (
                          <li key={index} className="p-3 bg-gray-100 rounded-md border border-gray-200 leading-relaxed">
                            <pre className='whitespace-pre-wrap font-sans'>{chunk}</pre>
                          </li>
                        ))}
                      </ol>
                    ) : (
                      <p className="text-gray-500">该知识库中没有文档内容。</p>
                    )
                  )}
                </div>
              </>
            ) : (
              <div className="flex items-center justify-center h-full text-gray-500">
                <p>请在左侧点击一个知识库以查看其内容。</p>
              </div>
            )}
          </div>
        </div>
        <div className="mt-4 pt-4 border-t flex justify-end">
            <button type="button" onClick={onClose} className="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600" disabled={isLoading}>
                关闭
            </button>
        </div>
      </div>
    </div>
  );
};

function App() {
  const reactFlowWrapper = useRef(null);
  const fileInputRef = useRef(null);
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const [reactFlowInstance, setReactFlowInstance] = useState(null);
  const [selectedNode, setSelectedNode] = useState(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [consoleOutput, setConsoleOutput] = useState('');
  const [statusMessage, setStatusMessage] = useState('');
  const [isApiModalOpen, setIsApiModalOpen] = useState(false);
  const [apiDetails, setApiDetails] = useState(null);
  const [isKbModalOpen, setIsKbModalOpen] = useState(false);

  const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);

  const onDragOver = useCallback((event) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
  }, []);

  const onDrop = useCallback(
    (event) => {
      event.preventDefault();
      const type = event.dataTransfer.getData('application/reactflow');
      if (!type) return;

      if (!reactFlowInstance) {
        console.error('React Flow 实例未初始化');
        return;
      }

      const position = reactFlowInstance.project({
        x: event.clientX - reactFlowWrapper.current.getBoundingClientRect().left,
        y: event.clientY - reactFlowWrapper.current.getBoundingClientRect().top,
      });
      
      const baseNodeName = type === 'start' ? '开始' : type === 'llm' ? 'LLM' : type === 'end' ? '结束' : type === 'branch' ? '条件分支' : type === 'code' ? '代码执行' : type === 'knowledge' ? '知识检索' : 'HTTP请求';

      const nodesOfType = nodes.filter(n => n.data.nodeType === type);
      let maxSuffix = 0;
      nodesOfType.forEach(n => {
        if (n.data.name && n.data.name.startsWith(baseNodeName)) {
          const suffixStr = n.data.name.substring(baseNodeName.length);
          if (suffixStr === '') {
            maxSuffix = Math.max(maxSuffix, 1);
          } else {
            const suffixNum = parseInt(suffixStr, 10);
            if (!isNaN(suffixNum)) {
              maxSuffix = Math.max(maxSuffix, suffixNum);
            }
          }
        }
      });

      const newCount = maxSuffix + 1;
      const suffix = newCount > 1 ? newCount.toString() : '';
      const nodeName = `${baseNodeName}${suffix}`;
      const outputVar = `${type}_node${suffix}_output`;
      
      const newNode = {
        id: getId(nodes),
        type: type,
        position,
        data: {
          label: nodeName,
          name: nodeName,
          nodeType: type,
          output_var: outputVar,
          ...(type === 'start' && { variables: {} }),
          ...(type === 'llm' && { model_url: '', model_name: '', api_key: '', system: '', user: '', video_url: '' }),
          ...(type === 'branch' && { variable: '', conditionType: '', conditionValue: '' }),
          ...(type === 'code' && {
            code: `def main(arg1: str, arg2: str) -> str:
    # 确保函数参数名与上方定义的“本地参数名”匹配
    result = str(arg1) + " " + str(arg2)
    return result`,
            inputVariables: {
              arg1: '',
              arg2: ''
            }
          }),
          ...(type === 'http' && { method: 'GET', url: '', headers: '{"Content-Type":"application/json","Authorization":"Bearer <token_placeholder>"}', data: '' }),
          ...(type === 'knowledge' && { query_variable: '', knowledge_base_id: '', top_k: 3, score_threshold: 0.2 }),
          ...(type === 'end' && { variables: {} }),
        },
        className: getNodeTypeClass(type),
        sourcePosition: Position.Right,
        targetPosition: Position.Left,
      };

      setNodes((nds) => [...nds, newNode]);
    },
    [reactFlowInstance, nodes, setNodes]
  );

  const onNodeClick = useCallback((event, node) => {
    setSelectedNode(node);
    setIsModalOpen(true);
  }, []);

  const onEdgeClick = useCallback((event, edge) => {
    setEdges((eds) => eds.filter((e) => e.id !== edge.id));
    setStatusMessage(`已删除连线 ${edge.source} -> ${edge.target}。`);
  }, [setEdges]);

  const updateNodeData = useCallback((nodeId, newData, isDelete = false) => {
    if (isDelete) {
      setNodes((nds) => nds.filter((n) => n.id !== nodeId));
      setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId));
      setSelectedNode(null);
      setIsModalOpen(false);
      setStatusMessage(`节点 ${nodeId} 已删除。`);
      return;
    }

    setNodes((nds) =>
      nds.map((node) =>
        node.id === nodeId
          ? {
              ...node,
              data: {
                ...node.data,
                ...newData,
                label: newData.name || node.data.name,
              },
            }
          : node
      )
    );
    setSelectedNode((prev) =>
      prev && prev.id === nodeId
        ? {
            ...prev,
            data: { ...prev.data, ...newData, label: newData.name || prev.data.name },
          }
        : prev
    );
    setStatusMessage('节点属性完成！');
  }, [setNodes, setEdges]);

  const closeModal = useCallback(() => {
    setIsModalOpen(false);
    setSelectedNode(null);
  }, []);

  const closeApiModal = useCallback(() => {
    setIsApiModalOpen(false);
  }, []);

  const saveWorkflow = useCallback(() => {
    if (reactFlowInstance) {
      const flow = reactFlowInstance.toObject();
      const jsonString = JSON.stringify(flow, null, 2);
      const blob = new Blob([jsonString], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = 'workflow.json';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
      setStatusMessage('工作流已下载为 JSON 文件。');
    }
  }, [reactFlowInstance]);

  const loadWorkflow = useCallback((event) => {
    const file = event.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (e) => {
        try {
          const flow = JSON.parse(e.target.result);
          if (flow.nodes && flow.edges) {
            setNodes(flow.nodes);
            setEdges(flow.edges);
            idCounter = Math.max(...flow.nodes.map((n) => parseInt(n.id.split('_')[1] || '0')).filter(Boolean), 0) + 1;
            setStatusMessage('工作流已从 JSON 文件导入。');
          } else {
            setStatusMessage('无效的工作流文件格式。');
          }
        } catch (error) {
          setStatusMessage('导入工作流失败：' + error.message);
        }
      };
      reader.readAsText(file);
      fileInputRef.current.value = ''; // 清空文件输入
    }
  }, [setNodes, setEdges]);

  const executeWorkflow = useCallback(async () => {
    if (!reactFlowInstance) {
      setStatusMessage('请先初始化表格。');
      return;
    }

    setStatusMessage('正在执行工作流...');
    setConsoleOutput('');

    const flow = reactFlowInstance.toObject();
    const hasStartNode = flow.nodes.some((node) => node.data.nodeType === 'start');
    const hasEndNode = flow.nodes.some((node) => node.data.nodeType === 'end');
    if (!hasStartNode) {
      setStatusMessage('错误：工作流必须包含一个“开始节点”！');
      setConsoleOutput('错误：无法执行工作流，缺少“开始节点”。');
      return;
    }
    if (!hasEndNode) {
      setStatusMessage('错误：工作流必须包含一个“结束节点”！');
      setConsoleOutput('错误：无法执行工作流，缺少“结束节点”。');
      return;
    }

    const workflowData = {
      nodes: flow.nodes.map((node) => ({
        id: node.id,
        type: node.data.nodeType,
        data: node.data,
        position: node.position,
      })),
      edges: flow.edges.map((edge) => ({
        id: edge.id,
        source: edge.source,
        target: edge.target,
        sourceHandle: edge.sourceHandle,
        targetHandle: edge.targetHandle,
      })),
      initial_context: {},
    };

    try {
      const response = await fetch(`${API_BASE_URL}/execute-workflow`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(workflowData),
      });

      const result = await response.json();
      if (response.ok) {
        const context = result.context;
        let errors = [];
        if (context) {
          for (const key in context) {
            if (key.endsWith('_outputs') && context[key] && context[key].error) {
              const nodeId = key.replace('_outputs', '');
              const errorNode = nodes.find(n => n.id === nodeId);
              const nodeName = errorNode ? errorNode.data.label : nodeId;
              const errorMessage = `节点 [${nodeName}] 发生错误:\n${context[key].error}`;
              errors.push(errorMessage);
            }
          }
        }

        if (errors.length > 0) {
          setConsoleOutput(errors.join('\n\n'));
          setStatusMessage('工作流执行失败！');
        } else {
          let formattedOutput = '';
          if (context) {
            const endNodeOutputKey = Object.keys(context).find((key) =>
              key.startsWith('end_node') && key.endsWith('_output')
            );
            if (endNodeOutputKey && context[endNodeOutputKey]) {
              const endOutput = context[endNodeOutputKey];
              if (endOutput.variables && Object.keys(endOutput.variables).length > 0) {
                const llmOutputKey = Object.keys(context).find((key) =>
                  key.startsWith('llm_node') && key.endsWith('_output')
                );
                if (llmOutputKey && context[llmOutputKey] && context[llmOutputKey].response) {
                  formattedOutput += context[llmOutputKey].response;
                } else {
                  formattedOutput += JSON.stringify(endOutput.variables, null, 2);
                }
              } else {
                formattedOutput += '无输出变量\n';
              }
            } else {
              formattedOutput += `(未找到结束节点的输出，请检查工作流配置)\n`;
            }
          }
          setConsoleOutput(formattedOutput || '无结束节点输出。');
          setStatusMessage('工作流执行完成！');
        }
      } else {
        setConsoleOutput(`错误: ${result.message || '未知错误'}\n${result.traceback || ''}`);
        setStatusMessage('工作流执行失败！');
      }
    } catch (error) {
      setConsoleOutput(`网络请求错误: ${error.message}`);
      setStatusMessage('网络请求失败！');
    }
  }, [reactFlowInstance, nodes]);

  const publishWorkflow = useCallback(async () => {
    if (!reactFlowInstance) {
      setStatusMessage('请先初始化表格。');
      return;
    }

    setStatusMessage('正在发布工作流...');
    const flow = reactFlowInstance.toObject();
    const hasStartNode = flow.nodes.some((node) => node.data.nodeType === 'start');
    const hasEndNode = flow.nodes.some((node) => node.data.nodeType === 'end');
    if (!hasStartNode || !hasEndNode) {
      setStatusMessage('错误：工作流必须包含“开始节点”和“结束节点”！');
      return;
    }

    try {
      const response = await fetch(`${API_BASE_URL}/save-workflow`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(flow),
      });
      const data = await response.json();
      if (response.ok) {
        setApiDetails(data);
        setStatusMessage('工作流已发布! 点击 "API" 获取详情。');
      } else {
        setStatusMessage(`发布工作流失败: ${data.error}`);
      }
    } catch (error) {
      setStatusMessage(`发布工作流失败: ${error.message}`);
    }
  }, [reactFlowInstance]);

  const handleViewApiClick = useCallback(() => {
    if (apiDetails) {
      setIsApiModalOpen(true);
    } else {
      setStatusMessage('请先发布一个工作流以查看API。');
    }
  }, [apiDetails]);

  return (
    <div className="workflow-container">
      <div className="logo-container">
        <h1 className="logo-text">VideoFlow</h1>
      </div>
      <div className="workflow-main-area">
        <Sidebar />
        <div className="reactflow-canvas-wrapper" ref={reactFlowWrapper}>
          <ReactFlow
            nodes={nodes}
            edges={edges}
            onNodesChange={onNodesChange}
            onEdgesChange={onEdgesChange}
            onConnect={onConnect}
            onInit={setReactFlowInstance}
            onDrop={onDrop}
            onDragOver={onDragOver}
            onNodeClick={onNodeClick}
            onEdgeClick={onEdgeClick}
            nodeTypes={nodeTypes}
            fitView
          >
            <MiniMap />
            <Controls />
            <Background variant="dots" gap={12} size={1} />
          </ReactFlow>
        </div>
      </div>
      <div className="workflow-footer-actions">
        <div className="run-actions">
          <span
            className={`status-message ${
              statusMessage.includes('完成') ? 'success' : statusMessage.includes('失败') || statusMessage.includes('错误') ? 'error' : ''
            }`}
          >
            {statusMessage || '等待运行...'}
          </span>
          <button onClick={() => setIsKbModalOpen(true)} className="px-4 py-2 bg-indigo-500 text-white rounded-md hover:bg-indigo-600">
            知识库
          </button>
          <button onClick={saveWorkflow} className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">
            导出
          </button>
          <label className="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 cursor-pointer">
            导入
            <input
              type="file"
              accept=".json"
              ref={fileInputRef}
              onChange={loadWorkflow}
              className="hidden"
            />
          </label>
          <button onClick={executeWorkflow} className="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600">
            运行
          </button>
          <button onClick={publishWorkflow} className="px-4 py-2 bg-purple-500 text-white rounded-md hover:bg-purple-600">
            发布
          </button>
          <button
            onClick={handleViewApiClick}
            disabled={!apiDetails}
            className="px-4 py-2 bg-cyan-500 text-white rounded-md hover:bg-cyan-600 disabled:bg-gray-400 disabled:cursor-not-allowed"
          >
            API
          </button>
        </div>
        <div className="console-output">
          <h4>执行结果:</h4>
          <pre>{consoleOutput || '工作流执行结果和日志将显示在这里。'}</pre>
        </div>
      </div>
      {isModalOpen && selectedNode && (
        <NodeModal
          selectedNode={selectedNode}
          updateNodeData={updateNodeData}
          closeModal={closeModal}
          allNodes={nodes}
          edges={edges}
        />
      )}
      <ApiModal
        isOpen={isApiModalOpen}
        onClose={closeApiModal}
        apiDetails={apiDetails}
      />
      <KnowledgeBaseModal
        isOpen={isKbModalOpen}
        onClose={() => setIsKbModalOpen(false)}
        apiBaseUrl={API_BASE_URL}
      />
    </div>
  );
}

export default App;